import math

from Base import *
from Vector import Vector3

ONE_THIRD = 1.0 / 3

def InterpolateBezier(degree, controlPoints, t):
    assert len(controlPoints) == degree + 1

    minus_t = 1 - t
    v = Vector3()
    for k in range(degree + 1):
        v += BinomialCoefficient(degree, k) * pow(minus_t, degree - k) * pow(t, k) * controlPoints[k]
    return v

class BezierSpline:
    def __init__(self, cps = [], degree = 3, knots = []):
        self.controlPoints = cps
        self.degree = degree
        self.knots = knots

    def ControlPoints(self):
        return self.controlPoints

    def AppendControlPoint(self, cp):
        self.controlPoints.append(cp)

    def ClearControlPoints(self):
        self.controlPoints = []

    def SegmentCount(self):
        return len(self.controlPoints) - 1 // self.degree

    def Interpolate(self, t):
        segmentCount = self.SegmentCount()
        segmentIndex = int(math.floor(t * segmentCount))
        t = t * segmentCount - segmentIndex
        startIndex = segmentIndex * self.degree
        return InterpolateBezier(self.degree, self.controlPoints[startIndex : startIndex + self.degree + 1], t)

class BSpline(BezierSpline):
    def SegmentCount(self):
        return len(self.controlPoints) - self.degree

    def AppendControlPoint(self, cp):
        self.controlPoints.append(cp)
        segmentCount = self.SegmentCount()
        if segmentCount > 0 and self.knots == []:
            self.knots = [0, 0, 0, 1, 1, 1]
        elif self.knots != []:
            self.knots[-2] += 1
            self.knots[-1] += 1
            self.knots.append(segmentCount)

    def Interpolate(self, t):
        segmentCount = self.SegmentCount()
        segmentIndex = int(math.floor(t * segmentCount))
        t = t * segmentCount - segmentIndex
        startIndex = segmentIndex
        p1 = self.controlPoints[startIndex + 1] * 2 * ONE_THIRD + self.controlPoints[startIndex + 2] * ONE_THIRD
        p2 = self.controlPoints[startIndex + 1] * ONE_THIRD + self.controlPoints[startIndex + 2] * ONE_THIRD * 2
        p0 = (self.controlPoints[startIndex] * ONE_THIRD + self.controlPoints[startIndex + 1] * ONE_THIRD * 2 + p1) * 0.5
        p3 = (self.controlPoints[startIndex + 3] * ONE_THIRD + self.controlPoints[startIndex + 2] * ONE_THIRD * 2 + p2) * 0.5
        return InterpolateBezier(self.degree, [p0, p1, p2, p3], t)

